/*
 * ses_framebuffer.c
 *
 *  Created on: May 20, 2011
 *      Author: Andre
 */

#include "ses_framebuffer.h"
#include <string.h>

static uint8_t framebuffer[DISPLAY_PAGES][DISPLAY_COLUMNS];
static uint8_t dirtypages;

/**
 * Initialize the framebuffer and the dirtypage buffer
 */
void ses_framebuffer_init(){
	uint8_t i;

	/* Clear Framebuffer */
	for(i=0 ; i < DISPLAY_PAGES ; i++){
		memset(framebuffer[i], 0, DISPLAY_COLUMNS);
	}

	/* Mark all pages dirty */
	dirtypages = 0xff;
}

/**
 * returns 1 if pixel is in display range and 0 if not
 */
static uint8_t _ses_framebuffer_rangecheck(uint8_t x, uint8_t y){
	if(x > DISPLAY_COLUMNS)return 0;
	if(y > (DISPLAY_PAGES * DISPLAY_ROWS_PER_PAGE))return 0;

	return 1;
}

/* Calculates the page to an y position */
static inline uint8_t _ses_framebuffer_page(uint8_t y){
	return y/DISPLAY_ROWS_PER_PAGE;
}

/* Calculates the column to an x position */
static inline uint8_t _ses_framebuffer_column(uint8_t x){
	return x;
}

void ses_setpixel(uint8_t x, uint8_t y){
	uint8_t page, column, bytepos;

	/* Check if pixel is in range */
	if(_ses_framebuffer_rangecheck(x, y) == 0)return;

	/* Calculate affected page */
	page = _ses_framebuffer_page(y);

	/* Calculate Column */
	column = _ses_framebuffer_column(x);

	/* Calculate Position in Byte */
	bytepos = y % DISPLAY_ROWS_PER_PAGE;

	/* Set the Bit in the framebuffer */
	framebuffer[page][column] |= (1<<bytepos);

	/* Mark the page dirty */
	dirtypages |= (1<<page);
}

void ses_clrpixel(uint8_t x, uint8_t y){
	uint8_t page, column, bytepos;

	/* Check if pixel is in range */
	if(_ses_framebuffer_rangecheck(x, y) == 0)return;

	/* Calculate affected page */
	page = _ses_framebuffer_page(y);

	/* Calculate Column */
	column = _ses_framebuffer_column(x);

	/* Calculate Position in Byte */
	bytepos = y % DISPLAY_ROWS_PER_PAGE;

	/* Set the Bit in the framebuffer */
	framebuffer[page][column] &= ~(1<<bytepos);

	/* Mark the page dirty */
	dirtypages |= (1<<page);
}

void ses_invpixel(uint8_t x, uint8_t y){
	uint8_t page, column, bytepos;

	/* Check if pixel is in range */
	if(_ses_framebuffer_rangecheck(x, y) == 0)return;

	/* Calculate affected page */
	page = _ses_framebuffer_page(y);

	/* Calculate Column */
	column = _ses_framebuffer_column(x);

	/* Calculate Position in Byte */
	bytepos = y % DISPLAY_ROWS_PER_PAGE;

	/* Set the Bit in the framebuffer */
	framebuffer[page][column] ^= (1<<bytepos);

	/* Mark the page dirty */
	dirtypages |= (1<<page);
}

/* Uploads the framebuffer to the display */
void ses_framebuffer_flush(){
	uint8_t i;

	/* For each Page ... */
	for(i=0; i<DISPLAY_PAGES ; i++){
		/* check if page has been marked dirty ... */
		if(!(dirtypages && (1<<i)))continue;

		/* ... then upload to display */
		display_write_page(i, framebuffer[i]);
	}

	/* Mark all pages clean */
	dirtypages = 0;
}
